package cn.t.keycloak.plugin.login.rest;

import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.RandomUtil;
import cn.t.keycloak.plugin.login.AuthConstants;
import cn.t.keycloak.plugin.login.jpa.VerifyPhoneEntity;
import cn.t.keycloak.plugin.login.spi.ISmsSenderProvider;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 陶敏麒
 * @date 2023/11/20 15:19
 */
@Slf4j
public class SmsResource {

    private final KeycloakSession session;

    public SmsResource(KeycloakSession session) {
        this.session = session;
    }

    @Path("/sendLoginSmsCode")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @POST
    public Map<String, Object> sendLoginSmsCode(String phoneNumber) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", 1);
        PhoneNumberUtil pl =  PhoneNumberUtil.getInstance();

        try {
            Phonenumber.PhoneNumber pObj = pl.parse(phoneNumber, null);

            if (!pl.isValidNumber(pObj)) {
                // 直接抛异常会返回html响应
                // throw new BadRequestException("手机号不合法");
                resultMap.put("msg", "手机号码不合法");
                return resultMap;
            }
            if (!this.existInAttr(phoneNumber)) {
                log.warn("手机[{}]未存在于系统", phoneNumber);
                resultMap.put("msg", "手机号码不存在!");
                return resultMap;
            }
            // 生成验证码
            String randomNumbers = RandomUtil.randomNumbers(6);
            int expires = this.session.getProvider(ISmsSenderProvider.class).sendVerificationCode(phoneNumber, randomNumbers);
            if (expires == 0) {
                resultMap.put("msg", "验证码已经发送过了");
                return resultMap;
            }
            log.info(">>>>向手机[{}]发送登录验证码[{}]", phoneNumber, randomNumbers);
            resultMap.put("code", 0);
            resultMap.put("data", expires);
            return resultMap;

        } catch (NumberParseException e) {
            resultMap.put("msg", "invalid phone number(+86xxxxxxxxxxx)");
            return resultMap;
        }
    }

    @Path("/sendMfaSmsCode")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @POST
    public Map<String, Object> sendSmsCode(String phoneNumber) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", 1);
        if (!Validator.isMobile(phoneNumber)) {
            // 直接抛异常会返回html响应
            // throw new BadRequestException("手机号不合法");
            resultMap.put("msg", "手机号码不合法");
            return resultMap;
        }

        if (!this.existInAttr(phoneNumber)) {
            log.warn("手机[{}]已经被认证了", phoneNumber);
            resultMap.put("msg", "手机号码已经被他人绑定了");
            return resultMap;
        }

        // 生成验证码
        String randomNumbers = RandomUtil.randomNumbers(6);
        log.info(">>>>开始向手机[{}]发送验证码[{}]", phoneNumber, randomNumbers);
        int expires = this.session.getProvider(ISmsSenderProvider.class).sendVerificationCode(phoneNumber, randomNumbers);
        resultMap.put("code", 0);
        resultMap.put("data", expires);
        return resultMap;
    }

    private boolean existsInCredential(String phoneNumber) {
        var em = this.session.getProvider(JpaConnectionProvider.class).getEntityManager();
        // 先查询是否存在
        List<VerifyPhoneEntity> list = em.createNamedQuery(VerifyPhoneEntity.QUERY_DUPLICATE, VerifyPhoneEntity.class)
                .setParameter("realmId", this.session.getContext().getRealm().getId())
                .setParameter(AuthConstants.FIELD_PHONE_NUMBER, phoneNumber).getResultList();
        return list!= null && !list.isEmpty();
    }

    private boolean existInAttr(String phoneNumber) {
        return this.session.users().searchForUserByUserAttributeStream(this.session.getContext().getRealm(), AuthConstants.FIELD_PHONE_NUMBER, phoneNumber)
                .findAny().isPresent();
    }
}
